/*******************************************************************************
 * Trace Recorder Library for Tracealyzer v3.1.0
 * Percepio AB, www.percepio.com
 *
 * trcStreamingRecorder.c
 *
 * The generic core of the trace recorder's streaming mode.
 *
 * Terms of Use
 * This file is part of the trace recorder library (RECORDER), which is the 
 * intellectual property of Percepio AB (PERCEPIO) and provided under a
 * license as follows.
 * The RECORDER may be used free of charge for the purpose of recording data
 * intended for analysis in PERCEPIO products. It may not be used or modified
 * for other purposes without explicit permission from PERCEPIO.
 * You may distribute the RECORDER in its original source code form, assuming
 * this text (terms of use, disclaimer, copyright notice) is unchanged. You are
 * allowed to distribute the RECORDER with minor modifications intended for
 * configuration or porting of the RECORDER, e.g., to allow using it on a 
 * specific processor, processor family or with a specific communication
 * interface. Any such modifications should be documented directly below
 * this comment block.  
 *
 * Disclaimer
 * The RECORDER is being delivered to you AS IS and PERCEPIO makes no warranty
 * as to its use or performance. PERCEPIO does not and cannot warrant the 
 * performance or results you may obtain by using the RECORDER or documentation.
 * PERCEPIO make no warranties, express or implied, as to noninfringement of
 * third party rights, merchantability, or fitness for any particular purpose.
 * In no event will PERCEPIO, its technology partners, or distributors be liable
 * to you for any consequential, incidental or special damages, including any
 * lost profits or lost savings, even if a representative of PERCEPIO has been
 * advised of the possibility of such damages, or for any claim by any third
 * party. Some jurisdictions do not allow the exclusion or limitation of
 * incidental, consequential or special damages, or the exclusion of implied
 * warranties or limitations on how long an implied warranty may last, so the
 * above limitations may not apply to you.
 *
 * Tabs are used for indent in this file (1 tab = 4 spaces)
 *
 * Copyright Percepio AB, 2016.
 * www.percepio.com
 ******************************************************************************/

#include "trcRecorder.h"

#if (TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_STREAMING)

#if (TRC_USE_TRACEALYZER_RECORDER == 1)

typedef struct{
	int16_t EventID;
	uint16_t EventCount;
	uint32_t TS;
} BaseEvent;

typedef struct{
  BaseEvent base;
  uint32_t param1;
} EventWithParam_1;

typedef struct{
  BaseEvent base;
  uint32_t param1;
  uint32_t param2;
} EventWithParam_2;

typedef struct{
  BaseEvent base;
  uint32_t param1;
  uint32_t param2;
  uint32_t param3;
} EventWithParam_3;

/* Used in event functions with variable number of parameters. */
typedef struct
{
  BaseEvent base;
  char data[60]; /* maximum payload size */
} largestEventType;

typedef struct{
  uint32_t psf;
  uint16_t version;
  uint16_t platform;
  uint32_t options;
  uint16_t symbolSize;
  uint16_t symbolCount;
  uint16_t objectDataSize;
  uint16_t objectDataCount;
} PSFHeaderInfo;


/* The size of each slot in the Symbol Table */
#define SYMBOL_TABLE_SLOT_SIZE (sizeof(uint32_t) + (((TRC_CFG_SYMBOL_MAX_LENGTH)+(sizeof(uint32_t)-1))/sizeof(uint32_t))*sizeof(uint32_t))

#define OBJECT_DATA_SLOT_SIZE (sizeof(uint32_t) + sizeof(uint32_t))

/* The total size of the Symbol Table */
#define SYMBOL_TABLE_BUFFER_SIZE ((TRC_CFG_SYMBOL_TABLE_SLOTS) * SYMBOL_TABLE_SLOT_SIZE)

/* The total size of the Object Data Table */
#define OBJECT_DATA_TABLE_BUFFER_SIZE ((TRC_CFG_OBJECT_DATA_SLOTS) * OBJECT_DATA_SLOT_SIZE)

/* The Symbol Table type - just a byte array */
typedef struct{
  uint8_t pSymbolTableBuffer[SYMBOL_TABLE_BUFFER_SIZE];
} SymbolTable;

/* The Object Data Table type - just a byte array */
typedef struct{
  uint8_t pObjectDataTableBuffer[OBJECT_DATA_TABLE_BUFFER_SIZE];
} ObjectDataTable;

typedef struct{
	uint8_t Status;
	uint16_t BytesRemaining;
	char* WritePointer;
} PageType;

/* Code used for "task address" when no task has started. (NULL = idle task) */
#define HANDLE_NO_TASK 2

#define PAGE_STATUS_FREE 0
#define PAGE_STATUS_WRITE 1
#define PAGE_STATUS_READ 2

#define PSF_ASSERT(_assert, _err) if (! (_assert)){ prvTraceError(_err); return; }

#define PSF_ERROR_NONE 0
#define PSF_ERROR_EVENT_CODE_TOO_LARGE 1
#define PSF_ERROR_ISR_NESTING_OVERFLOW 2
#define PSF_ERROR_DWT_NOT_SUPPORTED 3
#define PSF_ERROR_DWT_CYCCNT_NOT_SUPPORTED 4
#define PSF_ERROR_AUTO_ISR_END 5

/* Part of the PSF format - encodes the number of 32-bit params in an event */
#define PARAM_COUNT(n) ((n & 0xF) << 12)

/* The Symbol Table instance - keeps names of tasks and other named objects. */
static SymbolTable symbolTable = { { 0 } };

/* This points to the first unused entry in the symbol table. */
static uint32_t firstFreeSymbolTableIndex = 0;

/* The Object Data Table instance - keeps initial priorities of tasks. */
static ObjectDataTable objectDataTable = { { 0 } };

/* This points to the first unused entry in the object data table. */
static uint32_t firstFreeObjectDataTableIndex = 0;

/* Keeps track of ISR nesting */
static uint32_t ISR_stack[TRC_CFG_MAX_ISR_NESTING];

/* Keeps track of ISR nesting */
static int8_t ISR_stack_index = -1;

/* Any error that occured in the recorder (also creates User Event) */
static int errorCode = 0;

/* Counts the number of trace sessions (not yet used) */
static uint32_t SessionCounter = 0;

/* Master switch for recording (0 => Disabled, 1 => Enabled) */
uint32_t RecorderEnabled = 0;

/* Used to determine endian of data (big/little) */
static uint32_t PSFEndianessIdentifier = 0x50534600;

/* Used to interpret the data format */
static uint16_t FormatVersion = 0x0004;

/* The number of events stored. Used as event sequence number. */
static uint32_t eventCounter = 0;

/* The user event channel for recorder warnings, defined in trcKernelPort.c */
extern char* trcWarningChannel;

/* Remembers if an earlier ISR in a sequence of adjancent ISRs has triggered a task switch.
In that case, vTraceStoreISREnd does not store a return to the previously executing task. */
int32_t isPendingContextSwitch = 0;

uint32_t uiTraceTickCount = 0;
uint32_t timestampFrequency = 0;
uint32_t DroppedEventCounter = 0;	// Total number of dropped events (failed allocations)
uint32_t TotalBytesRemaining_LowWaterMark = TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT * TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE;
uint32_t TotalBytesRemaining = TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT * TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE;

PageType PageInfo[TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT];

char* EventBuffer = NULL;

/*******************************************************************************
 * NoRoomForSymbol
 *
 * Incremented on prvTraceSaveSymbol if no room for saving the symbol name. This
 * is used for storing the names of:
 * - Tasks
 * - Named ISRs (xTraceSetISRProperties)
 * - Named kernel objects (vTraceStoreKernelObjectName)
 * - User event channels (xTraceRegisterString)
 *
 * This variable should be zero. If not, it shows the number of missing slots so
 * far. In that case, increment SYMBOL_TABLE_SLOTS with (at least) this value.
 ******************************************************************************/
volatile uint32_t NoRoomForSymbol = 0;

/*******************************************************************************
 * NoRoomForObjectData
 *
 * Incremented on prvTraceSaveObjectData if no room for saving the object data,
 * i.e., the base priorities of tasks. There must be one slot for each task.
 * If not, this variable will show the difference.
 *
 * This variable should be zero. If not, it shows the number of missing slots so
 * far. In that case, increment OBJECT_DATA_SLOTS with (at least) this value.
 ******************************************************************************/
volatile uint32_t NoRoomForObjectData = 0;

/*******************************************************************************
 * LongestSymbolName
 *
 * Updated in prvTraceSaveSymbol. Should not exceed SYMBOL_MAX_LENGTH, otherwise
 * symbol names will be truncated. In that case, set SYMBOL_MAX_LENGTH to (at
 * least) this value.
 ******************************************************************************/
volatile uint32_t LongestSymbolName = 0;

/*******************************************************************************
 * MaxBytesTruncated
 *
 * Set in prvTraceStoreStringEvent if the total data payload exceeds 60 bytes,
 * including data arguments and the string. For user events, that is 52 bytes
 * for string and data arguments. In that is exceeded, the event is  truncated
 * (usually only the string, unless more than 15 parameters) and this variable
 * holds the maximum number of truncated bytes, from any event.
 ******************************************************************************/
volatile uint32_t MaxBytesTruncated = 0;

/* Internal common function for storing string events */
static void prvTraceStoreStringEventHelper(	int nArgs,
										uint16_t eventID,
										traceString userEvtChannel,
										const char* str,
										va_list vl);

/* Stores the header information on Start */
static void prvTraceStoreHeader(void);

/* Stores the symbol table on Start */
static void prvTraceStoreSymbolTable(void);

/* Stores the object table on Start */
static void prvTraceStoreObjectDataTable(void);

/* Store the Timestamp Config on Start */
static void prvTraceStoreTSConfig(void);

/* Store the current warnings */
static void prvTraceStoreWarnings(void);

/* Internal function for starting/stopping the recorder. */
static void prvSetRecorderEnabled(int isEnabled);

/* Mark the page read as complete. */
static void prvPageReadComplete(int pageIndex);

/* Retrieve a buffer page to write to. */
static int prvAllocateBufferPage(int prevPage);

/* Get the current buffer page index and remaining number of bytes. */
static int prvGetBufferPage(int32_t* bytesUsed);

/* Performs timestamping using definitions in trcHardwarePort.h */
static uint32_t prvGetTimestamp32(void);

/* Signal an error. */
void prvTraceError(int errCode);

/******************************************************************************
 * vTraceInstanceFinishedNow
 *
 * Creates an event that ends the current task instance at this very instant.
 * This makes the viewer to splits the current fragment at this point and begin
 * a new actor instance, even if no task-switch has occurred.
 *****************************************************************************/
void vTraceInstanceFinishedNow(void)
{
	prvTraceStoreEvent0(PSF_EVENT_IFE_DIRECT);
}

/******************************************************************************
 * vTraceInstanceFinishedNext
 *
 * Marks the current "task instance" as finished on the next kernel call.
 *
 * If that kernel call is blocking, the instance ends after the blocking event
 * and the corresponding return event is then the start of the next instance.
 * If the kernel call is not blocking, the viewer instead splits the current
 * fragment right before the kernel call, which makes this call the first event
 * of the next instance.
 *****************************************************************************/
void vTraceInstanceFinishedNext(void)
{
	prvTraceStoreEvent0(PSF_EVENT_IFE_NEXT);
}

/*******************************************************************************
 * xTraceRegisterString
 *
 * Stores a name for a user event channel, returns the handle.
 ******************************************************************************/
traceString xTraceRegisterString(const char* name)
{
    prvTraceSaveSymbol((void*)name, name);

	/* Always save in symbol table, if the recording has not yet started */
	prvTraceStoreStringEvent(1, PSF_EVENT_OBJ_NAME, (traceString)name, (uint32_t)name);

	return (traceString)name;
}

/*******************************************************************************
 * vTraceStoreKernelObjectName
 *
 * Parameter object: pointer to the Event Group that shall be named
 * Parameter name: the name to set (const string literal)
 *
 * Sets a name for a kernel object for display in Tracealyzer.
 ******************************************************************************/
void vTraceStoreKernelObjectName(void* object, const char* name)
{
	/* Always save in symbol table, if the recording has not yet started */
	prvTraceSaveSymbol(object, name);

	prvTraceStoreStringEvent(1, PSF_EVENT_OBJ_NAME, name, (uint32_t)object);
}

/******************************************************************************
 * vTraceSetFrequency
 *
 * Sets the clock rate of the timestamping timer, to give correct timeline.
 * This is normally not required, since the default value (TRC_HWTC_FREQ_HZ)
 * is normally correct. But if using custom setups, this may be necessary.
 *****************************************************************************/
void vTraceSetFrequency(uint32_t frequency)
{
	timestampFrequency = frequency;
}

/******************************************************************************
 * vTracePrint
 *
 * Generates "User Events", with unformatted text.
 *
 * User Events can be used for very efficient application logging, and are shown
 * as yellow labels in the main trace view.
 *
 * You may group User Events into User Event Channels. The yellow User Event 
 * labels shows the logged string, preceeded by the channel  name within 
 * brackets. For example:
 *
 *  "[MyChannel] Hello World!"
 *
 * The User Event Channels are shown in the View Filter, which makes it easy to
 * select what User Events you wish to display. User Event Channels are created
 * using xTraceRegisterString().
 *
 * Example:
 *
 *	 traceString chn = xTraceRegisterString("MyChannel");
 *	 ...
 *	 vTracePrint(chn, "Hello World!");
 *
 ******************************************************************************/
void vTracePrint(traceString chn, const char* str)
{
  va_list vl = { 0 };
  
  if (chn != NULL)
  {
    prvTraceStoreStringEventHelper(0, PSF_EVENT_USER_EVENT + 1, chn, str, vl);
  }
  else
  {
    prvTraceStoreStringEventHelper(0, PSF_EVENT_USER_EVENT, chn, str, vl);
  }
}

/******************************************************************************
 * vTracePrintF
 *
 * Generates "User Events", with formatted text and data, similar to a "printf".
 * It is very fast since the actual formatting is done on the host side when the
 * trace is displayed.
 *
 * User Events can be used for very efficient application logging, and are shown
 * as yellow labels in the main trace view.
 * An advantage of User Events is that data can be plotted in the "User Event
 * Signal Plot" view, visualizing any data you log as User Events, discrete
 * states or control system signals (e.g. system inputs or outputs).
 *
 * You may group User Events into User Event Channels. The yellow User Event 
 * labels show the logged string, preceeded by the channel name within brackets.
 * 
 * Example:
 *
 *  "[MyChannel] Hello World!"
 *
 * The User Event Channels are shown in the View Filter, which makes it easy to
 * select what User Events you wish to display. User Event Channels are created
 * using xTraceRegisterString().
 *
 * Example:
 *
 *	 traceString adc_uechannel = xTraceRegisterString("ADC User Events");
 *	 ...
 *	 vTracePrintF(adc_uechannel,
 *				 "ADC channel %d: %d volts",
 *				 ch, adc_reading);
 *
 * All data arguments are assumed to be 32 bt wide. The following formats are
 * supported in v3.0:
 * %d - signed integer. The following width and padding format is supported: "%05d" -> "-0042" and "%5d" -> "  -42"
 * %u - unsigned integer. The following width and padding format is supported: "%05u" -> "00042" and "%5u" -> "   42"
 * %X - hexadecimal (uppercase). The following width and padding format is supported: "%04X" -> "002A" and "%4X" -> "  2A"
 * %x - hexadecimal (lowercase). The following width and padding format is supported: "%04x" -> "002a" and "%4x" -> "  2a"
 * %s - string (currently, this must be an earlier stored symbol name)
 *
 * Up to 15 data arguments are allowed, with a total size of maximum 60 byte
 * including 8 byte for the base event fields and the format string. So with
 * one data argument, the maximum string length is 48 chars. If this is exceeded
 * the string is truncated (4 bytes at a time).
 *
 ******************************************************************************/
void vTracePrintF(traceString chn, const char* fmt, ...)
{
	va_list vl;
	int i = 0;

  int nArgs = 0;

  /* Count the number of arguments in the format string (e.g., %d) */
  for (i = 0; (fmt[i] != 0) && (i < 52); i++)
  {
    if (fmt[i] == '%')
    {
		if (fmt[i + 1] != '%')
		{
			nArgs++;        /* Found an argument */
		}
		
		i++;      /* Move past format specifier or non-argument '%' */
    }
  }

  va_start(vl, fmt);
  if (chn != NULL)
  {
    prvTraceStoreStringEventHelper(nArgs, PSF_EVENT_USER_EVENT + nArgs + 1, chn, fmt, vl);
  }
  else
  {
    prvTraceStoreStringEventHelper(nArgs, PSF_EVENT_USER_EVENT + nArgs, chn, fmt, vl);
  }
  va_end(vl);
}

/*******************************************************************************
 * xTraceSetISRProperties
 *
 * Stores a name and priority level for an Interrupt Service Routine, to allow
 * for better visualization. Returns a traceHandle used by vTraceStoreISRBegin. 
 *
 * Example:
 *	 #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
 *	 ...
 *	 traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
 *	 ...
 *	 void ISR_handler()
 *	 {
 *		 vTraceStoreISRBegin(Timer1Handle);
 *		 ...
 *		 vTraceStoreISREnd(0);
 *	 }
 *
 ******************************************************************************/
traceHandle xTraceSetISRProperties(const char* name, uint8_t priority)
{
	/* Save object data in object data table */
	prvTraceSaveObjectData((void*)name, priority);
        
	/* Note: "name" is used both as a string argument, and the address as ID */
	prvTraceStoreStringEvent(2, PSF_EVENT_DEFINE_ISR, name, name, priority);
        
	/* Always save in symbol table, if the recording has not yet started */
	prvTraceSaveSymbol((void*)name, name);
	
	return (traceHandle)name;
}

/*******************************************************************************
 * vTraceStoreISRBegin
 *
 * Registers the beginning of an Interrupt Service Routine, using a traceHandle
 * provided by xTraceSetISRProperties.
 *
 * Example:
 *	 #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
 *	 ...
 *	 traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
 *	 ...
 *	 void ISR_handler()
 *	 {
 *		 vTraceStoreISRBegin(Timer1Handle);
 *		 ...
 *		 vTraceStoreISREnd(0);
 *	 }
 *
 ******************************************************************************/
void vTraceStoreISRBegin(traceHandle handle)
{
	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();

	/* We are at the start of a possible ISR chain. 
	No context switches should have been triggered now. */
	if (ISR_stack_index == -1)
		isPendingContextSwitch = 0; 
	
	if (ISR_stack_index < TRC_CFG_MAX_ISR_NESTING - 1)
	{
		ISR_stack_index++;
		ISR_stack[ISR_stack_index] = (uint32_t)handle;
		prvTraceStoreEvent1(PSF_EVENT_ISR_BEGIN, (uint32_t)handle);
		TRACE_EXIT_CRITICAL_SECTION();
	}
	else
	{
		TRACE_EXIT_CRITICAL_SECTION();
		prvTraceError(PSF_ERROR_ISR_NESTING_OVERFLOW);
	}
}

/*******************************************************************************
 * vTraceStoreISREnd
 *
 * Registers the end of an Interrupt Service Routine.
 *
 * The parameter pendingISR indicates if the interrupt has requested a
 * task-switch (= 1), e.g., by signaling a semaphore. Otherwise (= 0) the 
 * interrupt is assumed to return to the previous context.
 *
 * Example:
 *	 #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt
 *	 traceHandle traceHandleIsrTimer1 = 0; // The ID set by the recorder
 *	 ...
 *	 traceHandleIsrTimer1 = xTraceSetISRProperties("ISRTimer1", PRIO_OF_ISR_TIMER1);
 *	 ...
 *	 void ISR_handler()
 *	 {
 *		 vTraceStoreISRBegin(traceHandleIsrTimer1);
 *		 ...
 *		 vTraceStoreISREnd(0);
 *	 }
 *
 ******************************************************************************/
void vTraceStoreISREnd(int isTaskSwitchRequired)
{
	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();

	/* Is there a pending task-switch? (perhaps from an earlier ISR) */
	isPendingContextSwitch |= isTaskSwitchRequired;

	if (ISR_stack_index > 0)
	{
		ISR_stack_index--;

		/* Store return to interrupted ISR (if nested ISRs)*/
		prvTraceStoreEvent1(PSF_EVENT_ISR_RESUME, (uint32_t)ISR_stack[ISR_stack_index]);
	}
	else
	{
		ISR_stack_index--;
		
		/* Store return to interrupted task, if a task switch has not been triggered by any interrupt */
		if (isPendingContextSwitch == 0)
		{
			prvTraceStoreEvent1(PSF_EVENT_TS_RESUME, (uint32_t)TRACE_GET_CURRENT_TASK());
		}
	}

	TRACE_EXIT_CRITICAL_SECTION();
}


/*******************************************************************************
 * xTraceGetLastError
 *
 * Returns the last error, if any.
 *****************************************************************************/
char* xTraceGetLastError(void)
{
	if (NoRoomForSymbol > 0)
	{
		return "TRC_CFG_SYMBOL_TABLE_SLOTS too small.";
	}

	if (LongestSymbolName > (TRC_CFG_SYMBOL_MAX_LENGTH))
	{
		return "TRC_CFG_SYMBOL_MAX_LENGTH too small.";
	}

	if (NoRoomForObjectData > 0)
	{
		return "TRC_CFG_OBJECT_DATA_SLOTS too small.";
	}

	if (MaxBytesTruncated > 0)
	{
		return "String or User Event too long.";
	}

	switch (errorCode)
	{
	case PSF_ERROR_EVENT_CODE_TOO_LARGE:
		return "An invalid event code was used.";
	case PSF_ERROR_ISR_NESTING_OVERFLOW:
		return "Too much ISR nesting.";
	case PSF_ERROR_DWT_NOT_SUPPORTED:
		return "DWT not supported by this chip.";
	case PSF_ERROR_DWT_CYCCNT_NOT_SUPPORTED:
		return "DWT_CYCCNT not supported by this chip.";
	}
	
	return "";
}

/*******************************************************************************
 * vTraceClearError
 *
 * Clears any errors.
 *****************************************************************************/
void vTraceClearError(void)
{
	NoRoomForSymbol = 0;
	LongestSymbolName = 0;
	NoRoomForObjectData = 0;
	MaxBytesTruncated = 0;
	errorCode = PSF_ERROR_NONE;
}

/*******************************************************************************
 * vTraceStop
 *
 * Stops the tracing.
 *****************************************************************************/
void vTraceStop(void)
{
	prvSetRecorderEnabled(0);
}

/******************************************************************************/
/*** INTERNAL FUNCTIONS *******************************************************/
/******************************************************************************/

/* Internal function for starting/stopping the recorder. */
static void prvSetRecorderEnabled(int isEnabled)
{
	TRACE_ALLOC_CRITICAL_SECTION();

  	void* currentTask = TRACE_GET_CURRENT_TASK();

	TRACE_ENTER_CRITICAL_SECTION();

    RecorderEnabled = isEnabled;

    if (currentTask == NULL)
    {
		currentTask = (void*)HANDLE_NO_TASK;
	}

	if (RecorderEnabled)
	{
        prvTraceOnBegin();
        
     	eventCounter = 0;
        ISR_stack_index = -1;
        prvTraceStoreHeader();
		prvTraceStoreSymbolTable();
    	prvTraceStoreObjectDataTable();
        prvTraceStoreEvent3(	PSF_EVENT_TRACE_START,
							(uint32_t)TRACE_GET_OS_TICKS(),
							(uint32_t)currentTask,
							SessionCounter++);
        prvTraceStoreTSConfig();
		prvTraceStoreWarnings();
	}
    else
    {
        prvTraceOnEnd();
    }

	TRACE_EXIT_CRITICAL_SECTION();
}

/* Stores the symbol table on Start */
static void prvTraceStoreSymbolTable()
{
	uint32_t i = 0;
	uint32_t j = 0;
	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();

	if (RecorderEnabled)
	{
		for (i = 0; i < sizeof(SymbolTable); i += SYMBOL_TABLE_SLOT_SIZE)
		{
            TRC_STREAM_PORT_ALLOCATE_EVENT(uint8_t, data, SYMBOL_TABLE_SLOT_SIZE);
            for (j = 0; j < SYMBOL_TABLE_SLOT_SIZE; j++)
            {
                    data[j] = symbolTable.pSymbolTableBuffer[i+j];
            }
			TRC_STREAM_PORT_COMMIT_EVENT(data, SYMBOL_TABLE_SLOT_SIZE);
		}
	}
	TRACE_EXIT_CRITICAL_SECTION();
}

/* Stores the object table on Start */
static void prvTraceStoreObjectDataTable()
{
	uint32_t i = 0;
	uint32_t j = 0;
	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();

	if (RecorderEnabled)
	{
		for (i = 0; i < sizeof(ObjectDataTable); i += OBJECT_DATA_SLOT_SIZE)
        {
            TRC_STREAM_PORT_ALLOCATE_EVENT(uint8_t, data, OBJECT_DATA_SLOT_SIZE);
            for (j = 0; j < OBJECT_DATA_SLOT_SIZE; j++)
            {
                    data[j] = objectDataTable.pObjectDataTableBuffer[i+j];
            }
            TRC_STREAM_PORT_COMMIT_EVENT(data, OBJECT_DATA_SLOT_SIZE);
        }
	}
	TRACE_EXIT_CRITICAL_SECTION();
}

/* Stores the header information on Start */
static void prvTraceStoreHeader()
{
  	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();

	if (RecorderEnabled)
	{
	  	TRC_STREAM_PORT_ALLOCATE_EVENT(PSFHeaderInfo, header, sizeof(PSFHeaderInfo));
		if (header != NULL)
		{
			header->psf = PSFEndianessIdentifier;
			header->version = FormatVersion;
			header->platform = TRACE_KERNEL_VERSION;
            header->options = 0;
            /* Lowest bit used for TRC_IRQ_PRIORITY_ORDER */
            header->options = header->options | (TRC_IRQ_PRIORITY_ORDER << 0);
			header->symbolSize = SYMBOL_TABLE_SLOT_SIZE;
			header->symbolCount = (TRC_CFG_SYMBOL_TABLE_SLOTS);
			header->objectDataSize = 8;
			header->objectDataCount = TRC_CFG_OBJECT_DATA_SLOTS;
			TRC_STREAM_PORT_COMMIT_EVENT(header, sizeof(PSFHeaderInfo));
		}
	}
	TRACE_EXIT_CRITICAL_SECTION();
}

/* Store the current warnings */
static void prvTraceStoreWarnings()
{
	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();

	if (RecorderEnabled)
	{
		if (NoRoomForSymbol > 0)
		{
			vTracePrintF(trcWarningChannel, "TRC_CFG_SYMBOL_TABLE_SLOTS too small. Add %d slots.", NoRoomForSymbol);
		}

		if (LongestSymbolName > 0)
		{
			if (LongestSymbolName > (TRC_CFG_SYMBOL_MAX_LENGTH))
			{
				vTracePrintF(trcWarningChannel, "TRC_CFG_SYMBOL_MAX_LENGTH too small. Add %d chars.", LongestSymbolName - (TRC_CFG_SYMBOL_MAX_LENGTH));
			}
		}

		if (NoRoomForObjectData > 0)
		{
			/* We don't know how many objects we actually need to make room for since NoRoomForObjectData can be increased multiple times for the same object! */
			vTracePrintF(trcWarningChannel, "TRC_CFG_OBJECT_DATA_SLOTS too small. Add more slots.");
		}

		if (MaxBytesTruncated > 0)
		{
			/* Some string event generated a too long string that was truncated.
			This may happen for the following functions:
			- vTracePrintF
			- vTraceStoreKernelObjectName
			- vTraceStoreUserEventChannelName
			- vTraceSetISRProperties

			A PSF event may store maximum 60 bytes payload, including data arguments
			and string characters. For User Events, also the User Event Channel ptr
			must be squeezed in, if a channel is specified. */

			vTracePrintF(trcWarningChannel, "String event too long, up to %d bytes truncated.", MaxBytesTruncated);
		}

		switch (errorCode)
		{
		case PSF_ERROR_EVENT_CODE_TOO_LARGE:
			break;
		case PSF_ERROR_ISR_NESTING_OVERFLOW:
			break;
		case PSF_ERROR_DWT_NOT_SUPPORTED:
			vTracePrintF(trcWarningChannel, "DWT not supported by this chip.");
			break;
		case PSF_ERROR_DWT_CYCCNT_NOT_SUPPORTED:
			vTracePrintF(trcWarningChannel, "DWT_CYCCNT not supported by this chip.");
			break;
		}
	}
	TRACE_EXIT_CRITICAL_SECTION();
}

/* Store an event with zero parameters (event ID only) */
void prvTraceStoreEvent0(uint16_t eventID)
{
  	TRACE_ALLOC_CRITICAL_SECTION();

	PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);

	TRACE_ENTER_CRITICAL_SECTION();

	if (RecorderEnabled)
	{
		eventCounter++;

  		TRC_STREAM_PORT_ALLOCATE_EVENT(BaseEvent, event, sizeof(BaseEvent));
		if (event != NULL)
		{
			event->EventID = eventID | PARAM_COUNT(0);
			event->EventCount = eventCounter;
			event->TS = prvGetTimestamp32();
			TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(BaseEvent));
		}

	}
	TRACE_EXIT_CRITICAL_SECTION();
}

/* Store an event with one 32-bit parameter (pointer address or an int) */
void prvTraceStoreEvent1(uint16_t eventID, uint32_t param1)
{
  	TRACE_ALLOC_CRITICAL_SECTION();

	PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);

	TRACE_ENTER_CRITICAL_SECTION();

	if (RecorderEnabled)
	{
		eventCounter++;
  		TRC_STREAM_PORT_ALLOCATE_EVENT(EventWithParam_1, event, sizeof(EventWithParam_1));
		if (event != NULL)
		{
			event->base.EventID = eventID | PARAM_COUNT(1);
			event->base.EventCount = eventCounter;
			event->base.TS = prvGetTimestamp32();
			event->param1 = (uint32_t)param1;
			TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(EventWithParam_1));
		}
	}
	TRACE_EXIT_CRITICAL_SECTION();
}

/* Store an event with two 32-bit parameters */
void prvTraceStoreEvent2(uint16_t eventID, uint32_t param1, uint32_t param2)
{
  	TRACE_ALLOC_CRITICAL_SECTION();

	PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);

	TRACE_ENTER_CRITICAL_SECTION();

	if (RecorderEnabled)
	{
		eventCounter++;

	  	TRC_STREAM_PORT_ALLOCATE_EVENT(EventWithParam_2, event, sizeof(EventWithParam_2));
		if (event != NULL)
		{
		  	event->base.EventID = eventID | PARAM_COUNT(2);
			event->base.EventCount = eventCounter;
			event->base.TS = prvGetTimestamp32();
			event->param1 = (uint32_t)param1;
			event->param2 = param2;
			TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(EventWithParam_2));
		}
	}
	TRACE_EXIT_CRITICAL_SECTION();
}

/* Store an event with three 32-bit parameters */
void prvTraceStoreEvent3(	uint16_t eventID,
						uint32_t param1,
						uint32_t param2,
						uint32_t param3)
{
  	TRACE_ALLOC_CRITICAL_SECTION();

	PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);

	TRACE_ENTER_CRITICAL_SECTION();

	if (RecorderEnabled)
	{
  		eventCounter++;

	  	TRC_STREAM_PORT_ALLOCATE_EVENT(EventWithParam_3, event, sizeof(EventWithParam_3));
		if (event != NULL)
		{
			event->base.EventID = eventID | PARAM_COUNT(3);
			event->base.EventCount = eventCounter;
			event->base.TS = prvGetTimestamp32();
			event->param1 = (uint32_t)param1;
			event->param2 = param2;
			event->param3 = param3;
			TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(EventWithParam_3));
		}
	}
	TRACE_EXIT_CRITICAL_SECTION();
}

/* Stores an event with <nParam> 32-bit integer parameters */
void prvTraceStoreEvent(int nParam, uint16_t eventID, ...)
{
    TRACE_ALLOC_CRITICAL_SECTION();
	va_list vl;
	int i;

	PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);

	TRACE_ENTER_CRITICAL_SECTION();

	if (RecorderEnabled)
	{
	  	int eventSize = sizeof(BaseEvent) + nParam * sizeof(uint32_t);

		eventCounter++;

	  	TRC_STREAM_PORT_ALLOCATE_DYNAMIC_EVENT(largestEventType, event, eventSize);
		if (event != NULL)
		{
			event->base.EventID = eventID | PARAM_COUNT(nParam);
			event->base.EventCount = eventCounter;
			event->base.TS = prvGetTimestamp32();

			va_start(vl, eventID);
  			for (i = 0; i < nParam; i++)
			{
		  		uint32_t* tmp = (uint32_t*) &(event->data[i * 4]);
	  			*tmp = va_arg(vl, uint32_t);
			}
			va_end(vl);

			TRC_STREAM_PORT_COMMIT_EVENT(event, eventSize);
		}
	}
	TRACE_EXIT_CRITICAL_SECTION();
}

/* Stories an event with a string and <nParam> 32-bit integer parameters */
void prvTraceStoreStringEvent(int nArgs, uint16_t eventID, const char* str, ...)
{
  	va_list vl;

	va_start(vl, str);
	prvTraceStoreStringEventHelper(nArgs, eventID, NULL, str, vl);
	va_end(vl);
}

/* Internal common function for storing string events */
static void prvTraceStoreStringEventHelper(	int nArgs,
										uint16_t eventID,
										traceString userEvtChannel,
										const char* str, va_list vl)
{
  	TRACE_ALLOC_CRITICAL_SECTION();
	int len;
  	int nWords;
	int nStrWords;
	int i;

	PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE);

	for (len = 0; (str[len] != 0) && (len < 52); len++); /* empty loop */
	
	/* The string length in multiples of 32 bit words (+1 for null character) */
	nStrWords = (len+1+3)/4;

	/* If a user event channel is specified, add in the list */
	if (userEvtChannel) nArgs++;

	/* The total number of 32-bit words needed for the whole payload */
	nWords = nStrWords + nArgs;

	if (nWords > 15) /* if attempting to store more than 60 byte (= max) */
	{
		/* Truncate event if too large. The	string characters are stored
		last, so usually only the string is truncated, unless there a lot
		of parameters... */

		/* Diagnostics ... */
		uint32_t bytesTruncated = (nWords - 15) * 4;

		if (bytesTruncated > MaxBytesTruncated)
		{
			MaxBytesTruncated = bytesTruncated;
		}

		nWords = 15;
		len = 15 * 4 - nArgs * 4;
	}

	TRACE_ENTER_CRITICAL_SECTION();

	if (RecorderEnabled)
	{
		int eventSize = sizeof(BaseEvent) + nWords * sizeof(uint32_t);

		eventCounter++;

	  	TRC_STREAM_PORT_ALLOCATE_DYNAMIC_EVENT(largestEventType, event, eventSize);
		if (event != NULL)
		{
			event->base.EventID = (eventID) | PARAM_COUNT(nWords);
			event->base.EventCount = eventCounter;
			event->base.TS = prvGetTimestamp32();

			/* 32-bit write-pointer for the data argument */
			uint32_t* data32 = (uint32_t*) &(event->data[0]);

			for (i = 0; i < nArgs; i++)
			{
				if ((userEvtChannel != NULL) && (i == 0))
				{
					/* First, add the User Event Channel if not NULL */
					data32[i] = (uint32_t)userEvtChannel;
				}
				else
				{
					/* Add data arguments... */
					data32[i] = va_arg(vl, uint32_t);
				}
			}

			for (i = 0; i < len; i++)
			{
		  		event->data[nArgs * 4 + i] = str[i];
			}

			if (len < (15 * 4 - nArgs * 4))
				event->data[nArgs * 4 + len] = 0;	/* Only truncate if we don't fill up the buffer completely */
			TRC_STREAM_PORT_COMMIT_EVENT(event, eventSize);
		}
	}
	
	TRACE_EXIT_CRITICAL_SECTION();
}

/* Saves a symbol name (task name etc.) in symbol table */
void prvTraceSaveSymbol(void *address, const char *name)
{
	uint32_t i;
	uint32_t foundSlot;
	uint32_t *ptrAddress;
	uint8_t *ptrSymbol;
	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();
	
	foundSlot = firstFreeSymbolTableIndex;

	/* First look for previous entries using this address */
	for (i = 0; i < firstFreeSymbolTableIndex; i += SYMBOL_TABLE_SLOT_SIZE)
	{
		ptrAddress = ((uint32_t*)&symbolTable.pSymbolTableBuffer[i]);
		if (*ptrAddress == (uint32_t)address)
		{
			foundSlot = i;
			break;
		}
	}

	if (foundSlot < SYMBOL_TABLE_BUFFER_SIZE)
	{
		*((uint32_t*)&symbolTable.pSymbolTableBuffer[foundSlot]) = (uint32_t)address;
		ptrSymbol = &symbolTable.pSymbolTableBuffer[foundSlot + sizeof(uint32_t)];

		for (i = 0; i < (TRC_CFG_SYMBOL_MAX_LENGTH); i++)
        {
			ptrSymbol[i] = name[i];	/* We do this first to ensure we also get the 0 termination, if there is one */

			if (name[i] == 0)
				break;
		}

		/* Check the length of "name", if longer than SYMBOL_MAX_LENGTH */
		while ((name[i] != 0) && i < 128)
		{
			i++;
		}

		/* Remember the longest symbol name, for diagnostic purposes */
		if (i > LongestSymbolName)
		{
			LongestSymbolName = i;
		}

		/* Is this the last entry in the symbol table? */
		if (foundSlot == firstFreeSymbolTableIndex)
		{
			firstFreeSymbolTableIndex += SYMBOL_TABLE_SLOT_SIZE;
		}
	}
	else
	{
		NoRoomForSymbol++;
	}

	TRACE_EXIT_CRITICAL_SECTION();
}

/* Deletes a symbol name (task name etc.) from symbol table */
void prvTraceDeleteSymbol(void *address)
{
	uint32_t i, j;
	uint32_t *ptr, *lastEntryPtr;
	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();

	for (i = 0; i < firstFreeSymbolTableIndex; i += SYMBOL_TABLE_SLOT_SIZE)
	{
		ptr = (uint32_t*)&symbolTable.pSymbolTableBuffer[i];
		if (*ptr == (uint32_t)address)
		{
			/* See if we have another entry in the table, and that this isn't already the last entry */
			if (firstFreeSymbolTableIndex > SYMBOL_TABLE_SLOT_SIZE && i != (firstFreeSymbolTableIndex - SYMBOL_TABLE_SLOT_SIZE))
			{
				/* Another entry is available, get pointer to the last one */
				lastEntryPtr = ((uint32_t*)&symbolTable.pSymbolTableBuffer[firstFreeSymbolTableIndex - SYMBOL_TABLE_SLOT_SIZE]);
				
				/* Copy last entry to this position */
				for (j = 0; j < (SYMBOL_TABLE_SLOT_SIZE) / sizeof(uint32_t); j++)
				{
					ptr[j] = lastEntryPtr[j];
				}

				/* For good measure we also zero out the original position */
				*lastEntryPtr = 0;
			}
			else
				*ptr = 0; /* No other entry found, or this is the last entry */

			/* Lower index */
			firstFreeSymbolTableIndex -= SYMBOL_TABLE_SLOT_SIZE;

			break;
		}
	}

	TRACE_EXIT_CRITICAL_SECTION();
}

/* Saves an object data entry (current task priority) in object data table */
void prvTraceSaveObjectData(void *address, uint32_t data)
{
	uint32_t i;
	uint32_t foundSlot;
	uint32_t *ptr;
	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();
	
	foundSlot = firstFreeObjectDataTableIndex;

	/* First look for previous entries using this address */
	for (i = 0; i < firstFreeObjectDataTableIndex; i += OBJECT_DATA_SLOT_SIZE)
	{
		ptr = ((uint32_t*)&objectDataTable.pObjectDataTableBuffer[i]);
		if (*ptr == (uint32_t)address)
		{
			foundSlot = i;
			break;
		}
	}

	if (foundSlot < OBJECT_DATA_TABLE_BUFFER_SIZE)
	{
		*(uint32_t*)&objectDataTable.pObjectDataTableBuffer[foundSlot] = (uint32_t)address;
		*(uint32_t*)&objectDataTable.pObjectDataTableBuffer[foundSlot + sizeof(uint32_t)] = data;

		/* Is this the last entry in the object data table? */
		if (foundSlot == firstFreeObjectDataTableIndex)
		{
			firstFreeObjectDataTableIndex += OBJECT_DATA_SLOT_SIZE;
		}
	}
	else
	{
		NoRoomForObjectData++;
	}

	TRACE_EXIT_CRITICAL_SECTION();
}

/* Removes an object data entry (task base priority) from object data table */
void prvTraceDeleteObjectData(void *address)
{
	uint32_t i, j;
	uint32_t *ptr, *lastEntryPtr;
	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();

	for (i = 0; i < firstFreeObjectDataTableIndex; i += OBJECT_DATA_SLOT_SIZE)
	{
		ptr = (uint32_t*)&objectDataTable.pObjectDataTableBuffer[i];
		if (*ptr == (uint32_t)address)
		{
			/* See if we have another entry in the table, and that this isn't already the last entry */
			if (firstFreeObjectDataTableIndex > OBJECT_DATA_SLOT_SIZE && i != (firstFreeObjectDataTableIndex - OBJECT_DATA_SLOT_SIZE))
			{
				/* Another entry is available, get pointer to the last one */
				lastEntryPtr = ((uint32_t*)&objectDataTable.pObjectDataTableBuffer[firstFreeObjectDataTableIndex - OBJECT_DATA_SLOT_SIZE]);
				
				/* Copy last entry to this position */
				for (j = 0; j < (OBJECT_DATA_SLOT_SIZE) / sizeof(uint32_t); j++)
				{
					ptr[j] = lastEntryPtr[j];
				}

				/* For good measure we also zero out the original position */
				*lastEntryPtr = 0;
			}
			else
				*ptr = 0; /* No other entry found, or this is the last entry */

			/* Lower index */
			firstFreeObjectDataTableIndex -= OBJECT_DATA_SLOT_SIZE;

			break;
		}
	}

	TRACE_EXIT_CRITICAL_SECTION();
}

/* Checks if the provided command is a valid command */
int prvIsValidCommand(TracealyzerCommandType* cmd)
{
  	uint16_t checksum = (uint16_t)(0xFFFF - (	cmd->cmdCode +
												cmd->param1 +
												cmd->param2 +
												cmd->param3 +
												cmd->param4 +
												cmd->param5));

	if (cmd->checksumMSB != (unsigned char)(checksum >> 8))
		return 0;

	if (cmd->checksumLSB != (unsigned char)(checksum & 0xFF))
		return 0;

	if (cmd->cmdCode > CMD_LAST_COMMAND)
		return 0;

	return 1;
}

/* Executed the received command (Start or Stop) */
void prvProcessCommand(TracealyzerCommandType* cmd)
{
  	switch(cmd->cmdCode)
	{
		case CMD_SET_ACTIVE:
		  	prvSetRecorderEnabled(cmd->param1);
		  	break;
		default:
		  	break;
	}
}

/* Called on critical errors in the recorder. Stops the recorder! */
void prvTraceError(int errCode)
{
	if (! errorCode)
	{
		errorCode = errCode;
		prvTraceStoreWarnings();
		vTracePrintF(trcWarningChannel, "Error detected. Stopped recorder.");

		prvSetRecorderEnabled(0);
	}
}

/* If ARM Cortex-M3, M4 or M7, make sure the DWT unit is initialized. */
#if (TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_ARM_Cortex_M && defined (TRC_REG_DWT_CYCCNT))

void prvTraceInitCortexM()
{
	/* Make sure DWT is enabled is enabled, if supported */
	TRC_REG_DEMCR |= TRC_DEMCR_TRCENA;

	do
	{
		/* Verify that DWT is supported */
		if (TRC_REG_DEMCR == 0)
		{
			prvTraceError(PSF_ERROR_DWT_NOT_SUPPORTED);
			break;
		}

		/* Verify that DWT_CYCCNT is supported */
		if (TRC_REG_DWT_CTRL & TRC_DWT_CTRL_NOCYCCNT)
		{
			prvTraceError(PSF_ERROR_DWT_CYCCNT_NOT_SUPPORTED);
			break;
		}

		/* Reset the cycle counter */
		TRC_REG_DWT_CYCCNT = 0;

		/* Enable the cycle counter */
		TRC_REG_DWT_CTRL |= TRC_DWT_CTRL_CYCCNTENA;

	} while(0);	/* breaks above jump here */
}
#endif

/* Performs timestamping using definitions in trcHardwarePort.h */
static uint32_t prvGetTimestamp32(void)
{
#if ((TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_INCR) || (TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_DECR))
	return TRC_HWTC_COUNT;
#else /* ((TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_INCR) || (TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_DECR)) */
	int ticks = TRACE_GET_OS_TICKS();
	return (TRC_HWTC_COUNT & 0x00FFFFFF) + ((ticks & 0x000000FF) << 24);
#endif /* ((TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_INCR) || (TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_DECR)) */
}

/* Store the Timestamp Config event */
static void prvTraceStoreTSConfig(void)
{
	/* If not overridden using vTraceSetFrequency, use default value */
	if (timestampFrequency == 0)
	{
		timestampFrequency = TRC_HWTC_FREQ_HZ;
	}
	
	prvTraceStoreEvent(4, 
						PSF_EVENT_TS_CONFIG,
						timestampFrequency,
						(uint32_t)TRACE_TICK_RATE_HZ,
						(int32_t)TRC_HWTC_TYPE,
						(uint32_t)TRC_CFG_ISR_TAILCHAINING_THRESHOLD);
}

/* Retrieve a buffer page to write to. */
static int prvAllocateBufferPage(int prevPage)
{
	int index;
	int count = 0;

	index = (prevPage + 1) % TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT;

	while((PageInfo[index].Status != PAGE_STATUS_FREE) && (count ++ < TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT))
	{
		index = (index + 1) % TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT;
	}

	if (PageInfo[index].Status == PAGE_STATUS_FREE)
	{
		return index;
	}

	return -1;
}

/* Mark the page read as complete. */
static void prvPageReadComplete(int pageIndex)
{
  	TRACE_ALLOC_CRITICAL_SECTION();

	TRACE_ENTER_CRITICAL_SECTION();
	PageInfo[pageIndex].BytesRemaining = TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE;
	PageInfo[pageIndex].WritePointer = &EventBuffer[pageIndex * TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE];
	PageInfo[pageIndex].Status = PAGE_STATUS_FREE;

	TotalBytesRemaining += TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE;

	TRACE_EXIT_CRITICAL_SECTION();
}

/* Get the current buffer page index and remaining number of bytes. */
static int prvGetBufferPage(int32_t* bytesUsed)
{
	static int8_t lastPage = -1;
	int count = 0;
  	int8_t index = (lastPage + 1) % TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT;

	while((PageInfo[index].Status != PAGE_STATUS_READ) && (count++ < TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT))
	{
		index = (index + 1) % TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT;
	}

	if (PageInfo[index].Status == PAGE_STATUS_READ)
	{
		*bytesUsed = TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE - PageInfo[index].BytesRemaining;
		lastPage = index;
		return index;
	}

	*bytesUsed = 0;

	return -1;
}

/*******************************************************************************

int32_t prvPagedEventBufferTransfer(int32_t (*writeFunc)(void* data,
                                                        uint32_t size),
                                            int32_t* nofBytes)

Transfers one block of trace data, if available for reading. Returns the number
of bytes transfered, or a negative error code. If data was transferred (return
value > 0), it can be good to call this function again until all data available
has been transfered.

This function is intended to be called by a periodic task with a suitable
delay (e.g. 10-100 ms).

Return value: returnvalue of writeFunc (0 == OK)

Parameters:

- writeFunc
Function pointer (example: int32_t write(void* data, uint32_t size))
The function passed as writeFunc should write "size" bytes from "data" to the
socket/file/channel, and return a status code where 0 means OK,
and any other non-zero value means an error.

- int32_t* nofBytes
Pointer to an integer assigned the number of bytes that was transfered.

*******************************************************************************/
int32_t prvPagedEventBufferTransfer(int32_t (*writeFunc)(void* data, uint32_t size, int32_t* ptrBytesWritten), int32_t* nofBytes)
{
	int8_t pageToTransfer = -1;
    int32_t transferred = 0;
    int32_t size = 0;
    
    pageToTransfer = prvGetBufferPage(nofBytes);
    size = *nofBytes;	// The number of bytes we want to transfer
    transferred = 0;	// The number of bytes we have transferred so far

    if (pageToTransfer > -1)
    {
        while (1) // Keep going until we have transferred all that we intended to
        {
			if (writeFunc(&EventBuffer[pageToTransfer * TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE + transferred], size - transferred, nofBytes) == 0)
			{
				// Write was successful. Update the number of transferred bytes.
				transferred += *nofBytes;
				if (size == transferred)
				{
					// All bytes have been transferred. Mark as Complete and return.
                    *nofBytes = transferred;
					prvPageReadComplete(pageToTransfer);
					return 0;
				}
			}
			else
			{
				*nofBytes = 0;
				return 1;
			}
		}
	}
	return 0;
}

/*******************************************************************************

void* prvPagedEventBufferGetWritePointer(int sizeOfEvent)

Returns a pointer to an available location in the buffer able to store the
requested size.

Return value: The pointer.

Parameters:

- sizeOfEvent
The size of the event that is to be placed in the buffer.

*******************************************************************************/
void* prvPagedEventBufferGetWritePointer(int sizeOfEvent)
{
	void* ret;
	static int currentWritePage = -1;

	if (currentWritePage == -1)
	{
	    currentWritePage = prvAllocateBufferPage(currentWritePage);
		if (currentWritePage == -1)
		{
		  	DroppedEventCounter++;
			return NULL;
		}
	}

    if (PageInfo[currentWritePage].BytesRemaining - sizeOfEvent < 0)
	{
		PageInfo[currentWritePage].Status = PAGE_STATUS_READ;

		TotalBytesRemaining -= PageInfo[currentWritePage].BytesRemaining; // Last trailing bytes

		if (TotalBytesRemaining < TotalBytesRemaining_LowWaterMark)
		  TotalBytesRemaining_LowWaterMark = TotalBytesRemaining;

		currentWritePage = prvAllocateBufferPage(currentWritePage);
		if (currentWritePage == -1)
		{
		  DroppedEventCounter++;
		  return NULL;
		}
	}
	ret = PageInfo[currentWritePage].WritePointer;
	PageInfo[currentWritePage].WritePointer += sizeOfEvent;
	PageInfo[currentWritePage].BytesRemaining -= sizeOfEvent;

	TotalBytesRemaining -= sizeOfEvent;

	if (TotalBytesRemaining < TotalBytesRemaining_LowWaterMark)
		TotalBytesRemaining_LowWaterMark = TotalBytesRemaining;

	return ret;
}

/*******************************************************************************

void prvPagedEventBufferInit(char* buffer)

Assigns the buffer to use and initializes the PageInfo structure.

Return value: void

Parameters:

- buffer
Pointer to the buffer location that is dynamically or statically allocated by
the caller.

*******************************************************************************/
void prvPagedEventBufferInit(char* buffer)
{
  	TRACE_ALLOC_CRITICAL_SECTION();
  	int i;
    
    EventBuffer = buffer;
    
	TRACE_ENTER_CRITICAL_SECTION();
	for (i = 0; i < TRC_CFG_PAGED_EVENT_BUFFER_PAGE_COUNT; i++)
	{
		PageInfo[i].BytesRemaining = TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE;
		PageInfo[i].WritePointer = &EventBuffer[i * TRC_CFG_PAGED_EVENT_BUFFER_PAGE_SIZE];
		PageInfo[i].Status = PAGE_STATUS_FREE;
	}
	TRACE_EXIT_CRITICAL_SECTION();
}

#endif /*(TRC_USE_TRACEALYZER_RECORDER == 1)*/

#endif /*(TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_STREAMING)*/
